﻿using OfficeOpenXml;
using OfficeOpenXml.Style;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace WAU.Utils.ExcelEpplusUtils
{
    /// <summary>
    /// 导出 Excel 项
    /// </summary>
    public class ExportExcelItem
    {
        /// <summary>
        /// sheet 的名称
        /// </summary>
        public string SheetName { get; set; }

        /// <summary>
        /// 数据集合
        /// </summary>
        public List<object> DataList { get; set; }

        /// <summary>
        /// 处理前的委托
        /// </summary>
        public Func<ExcelWorksheet, bool> StartHandler { get; set; }

        /// <summary>
        /// 处理数据的委托
        /// </summary>
        public Func<PropertyInfo, object, ExcelRange, bool> DataHandler { get; set; }

        /// <summary>
        /// 全部处理完成后的委托
        /// </summary>
        public Func<ExcelWorksheet, bool> FinishHandler { get; set; }
        /// <summary>
        /// 开始写入的行号
        /// </summary>
        public int StartNumber { get; set; }
    }

    /// <summary>
    /// 导出 Excel 工具类
    /// </summary>
    public class ExportExcelUtil
    {
        /// <summary>
        /// Excel 导出时的字典项（与 ExportExcelColumnAttribute 中的属性对应）
        /// </summary>
        private class ExcelExportInfoItem
        {
            public PropertyInfo PropertyInfo { get; set; }
            public int Order { get; set; }
            public string ColumnName { get; set; }
            public double WidthFactor { get; set; }
            public string DateTimeFormatPattern { get; set; }

            /// <summary>
            /// 生成导出信息序列
            /// </summary>
            /// <param name="dataType"></param>
            /// <returns></returns>
            public static ExcelExportInfoItem[] GenerateInfoArray(Type dataType)
            {
                PropertyInfo[] propertyInfos = dataType.GetProperties();
                ExcelExportInfoItem[] resultArray = propertyInfos.Select(a =>
                {
                    var attribute = a.GetCustomAttribute<ExportExcelColumnAttribute>();

                    if (attribute == null)
                    {
                        return null;
                    }

                    return new ExcelExportInfoItem
                    {
                        PropertyInfo = a,
                        ColumnName = attribute.ColumnName,
                        Order = attribute.Order,
                        WidthFactor = attribute.WidthFactor,
                        DateTimeFormatPattern = attribute.DateTimeFormatPattern,
                    };
                }).Where(info => info != null).OrderBy(a => a.Order).ToArray();

                return resultArray;
            }
        }

        /// <summary>
        /// 导出 Excel
        /// </summary>
        /// <param name="newFileFullName"></param>
        /// <param name="sheetName"></param>
        /// <param name="dataList"></param>
        /// <param name="startRowNumber"></param>
        /// <param name="startHandler"></param>
        /// <param name="dataHandler"></param>
        /// <param name="finishHandler"></param>
        public static void Export<T>(string newFileFullName, string sheetName, List<T> dataList,
            int startRowNumber = 1,
            Func<ExcelWorksheet, bool> startHandler = null,
            Func<PropertyInfo, object, ExcelRange, bool> dataHandler = null,
            Func<ExcelWorksheet, bool> finishHandler = null)
        {
            using (ExcelPackage package = new ExcelPackage(new FileInfo(newFileFullName)))
            {
                ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(sheetName);     //创建worksheet

                if (startHandler != null)
                {
                    startHandler(worksheet);
                }

                ExcelExportInfoItem[] infoItems = ExcelExportInfoItem.GenerateInfoArray(typeof(T));

                // 画所有标题行
                for (int titleIndex = 0; titleIndex < infoItems.Length; titleIndex++)
                {
                    worksheet.Cells[startRowNumber, titleIndex + 1].Value = infoItems[titleIndex].ColumnName;
                    worksheet.Column(titleIndex + 1).Width = infoItems[titleIndex].WidthFactor * 20;
                    worksheet.Cells[startRowNumber, titleIndex + 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                    worksheet.Cells[startRowNumber, titleIndex + 1].Style.Font.Bold = true;
                    worksheet.Cells[startRowNumber, titleIndex + 1].Style.Border.BorderAround(ExcelBorderStyle.Thin);
                }

                foreach (var data in dataList)
                {
                    for (int infoItemIndex = 0; infoItemIndex < infoItems.Length; infoItemIndex++)
                    {
                        PropertyInfo pi = infoItems[infoItemIndex].PropertyInfo;

                        if (pi != null)
                        {
                            object value = pi.GetValue(data);

                            if (dataHandler == null || !dataHandler(pi, value, worksheet.Cells[startRowNumber, infoItemIndex + 1]))
                            {
                                BasicDataHandler(pi, value, worksheet.Cells[startRowNumber, infoItemIndex + 1]);
                            }
                        }
                        else
                        {
                            NullDataHandler(worksheet.Cells[startRowNumber, infoItemIndex + 1]);
                        }

                        worksheet.Cells[startRowNumber, infoItemIndex + 1].Style.Border.BorderAround(ExcelBorderStyle.Thin);
                    }
                    startRowNumber += 1;
                }

                if (finishHandler != null)
                {
                    finishHandler(worksheet);
                }

                package.Save();
            }
        }

        /// <summary>
        /// 基本的数据处理逻辑
        /// </summary>
        /// <param name="pi"></param>
        /// <param name="value"></param>
        /// <param name="cell"></param>
        /// <param name="dateTimeFormatPattern"></param>
        public static void BasicDataHandler(PropertyInfo pi, Object value, ExcelRange cell,
            string dateTimeFormatPattern = "yyyy-MM-dd HH:mm:ss")
        {
            Type pt = pi.PropertyType;

            if (value == null)
            {
                cell.Value = string.Empty;
                return;
            }

            if (pt == typeof(DateTime) || pt == typeof(DateTime?))
            {
                cell.Value = ((DateTime)value).ToString(dateTimeFormatPattern);
            }
            else
            {
                cell.Value = value;
            }

            cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
            cell.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
        }

        /// <summary>
        /// 空数据的基本处理逻辑
        /// </summary>
        /// <param name="worksheet"></param>
        /// <param name="keyName"></param>
        /// <param name="pi"></param>
        /// <param name="value"></param>
        /// <param name="cell"></param>
        public static void NullDataHandler(ExcelRange cell)
        {
            cell.Value = string.Empty;
            cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
            cell.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
        }
    }

    /// <summary>
    /// 导出 Excel 时属性的 Attribute
    /// </summary>
    public class ExportExcelColumnAttribute : Attribute
    {
        /// <summary>
        /// 列名
        /// </summary>
        public string ColumnName { get; set; }
        /// <summary>
        /// 排序号
        /// </summary>
        public int Order { get; set; }
        /// <summary>
        /// 宽度系数
        /// </summary>
        public double WidthFactor { get; set; }
        /// <summary>
        /// 日期格式化参数
        /// </summary>
        public string DateTimeFormatPattern { get; set; }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="widthFactor"></param>
        /// <param name="datetimeFormatPattern"></param>
        public ExportExcelColumnAttribute(string columnName, double widthFactor = 1, string datetimeFormatPattern = null)
        {
            this.ColumnName = columnName;
            this.WidthFactor = widthFactor;
            this.DateTimeFormatPattern = datetimeFormatPattern;
        }
    }
}
